package com.turbo.cloud.core.utils;


import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * conversion 工具类
 */
public class ConverterUtil {

    private static final Pattern PATTERN_UNDERLINE = Pattern.compile("([A-Za-z\\d]+)(_)?");
    private static final Pattern PATTERN_CAMEL = Pattern.compile("[A-Z]([a-z\\d]+)?");
    private static final ObjectMapper jsonObjectMapper = new ObjectMapper().findAndRegisterModules().setSerializationInclusion(JsonInclude.Include.NON_NULL).registerModules(new JavaTimeModule(), new Jdk8Module()).disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

    public static String bean2JsonWithoutNull(Object bean) {
        String result = null;
        try {
            result = jsonObjectMapper.writeValueAsString(bean);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("bean2JsonWithoutNull 转换json异常");
        }
        if (result != null && result.length() > 4096) {
            result = result.substring(0, 4096) + " ......[length = " + result.length() + "]";
        }
        return result;
    }

    public static Object beanEmptyStr2Null(Object bean) {
        Assert.notNull(bean, "bean not be null");
        Class<?> beanClass = bean.getClass();
        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(beanClass);
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Method setter = property.getWriteMethod();
            try {
                Object value = getter != null ? getter.invoke(bean) : null;
                if (value instanceof String) {
                    String v = (String) value;
                    if (v != null && v.equals("")) {
                        setter.invoke(bean, (String) null);
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

        }
        return bean;
    }

    /**
     * 下划线转驼峰
     *
     * @param source 要转换的字符串
     * @return
     */
    public static String underline2Camel(String source) {
        if (null == source || "".equals(source)) {
            return "";
        }
        StringBuffer sb = new StringBuffer();
        Matcher matcher = PATTERN_UNDERLINE.matcher(source);
        while (matcher.find()) {
            String word = matcher.group();
            //匹配到到第一个字符串首字母小写
            sb.append(matcher.start() == 0 ? Character.toLowerCase(word.charAt(0)) : Character.toUpperCase(word.charAt(0)));
            int index = word.lastIndexOf('_');
            if (index > 0) {
                sb.append(word.substring(1, index).toLowerCase());
            } else {
                sb.append(word.substring(1).toLowerCase());
            }
        }
        return sb.toString();
    }

    /**
     * 驼峰转下划线
     *
     * @param source 要转换的字符串
     * @return 转换后的字符串
     */
    public static String camel2Underline(String source) {
        if (null == source || "".equals(source)) {
            return "";
        } else {
            if (source.contains("_")) {
                return source;
            }
        }

        //首字母转成大写
        source = String.valueOf(source.charAt(0)).toUpperCase().concat(source.substring(1));
        StringBuffer sb = new StringBuffer();
        Matcher matcher = PATTERN_CAMEL.matcher(source);
        while (matcher.find()) {
            String word = matcher.group();
            sb.append(word.toLowerCase());
            sb.append(matcher.end() == source.length() ? "" : "_");
        }
        return sb.toString();
    }

    /**
     * map 转简单实体
     *
     * @param map
     * @param cls
     * @return obj
     */
    public static Object convertMap(Map<String, Object> map, Class cls) throws Exception {
        Assert.notNull(map, "convertMap map is null");
        Field[] fields = cls.getDeclaredFields();
        Object obj = cls.newInstance();
        for (Field field : fields) {
            field.setAccessible(true);
            String name = field.getName();
            if (map.containsKey(name)) {
                field.set(obj, map.get(name));
            }
        }
        return obj;
    }

    public static <S> void trim(S dto) {
        Assert.notNull(dto, "trim dto is null");
        Class<?> clazz = dto.getClass();
        Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                if (field.getType().getTypeName().equals("java.lang.String")) {
                    String value = (String) field.get(dto);
                    if (value != null) {
                        field.set(dto, value.trim().replaceAll("[\\p{C}]", "").replaceAll("[\\p{Cntrl}\\p{Cc}\\p{Cf}\\p{Co}\\p{Cn}]", ""));
//                                .replaceAll("[\\r\\n\\t]", ""));
                    }
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException("ConverterUtil trim(T dto) error", e);
        }
    }

    public static void copyProperties(Object source, Object target) {
        copyPropertiesIgnoreNull(source, target, null);
    }

    public static void copyPropertiesIgnoreNull(Object source, Object target, String[] ignoreProperties) {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
        List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
        PropertyDescriptor[] var7 = targetPds;
        int var8 = targetPds.length;

        for (int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
                            Object value = readMethod.invoke(source);
                            if (null == value) {
                                continue;
                            } else {
                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                    writeMethod.setAccessible(true);
                                }
                                if (value instanceof String) {
                                    value = ((String) value).trim();
                                }
                                writeMethod.invoke(target, value);
                            }
                        } catch (Throwable var15) {
                            throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
                        }
                    }
                }
            }
        }

    }

    /**
     * 两种List Dto之间的转换
     *
     * @param sourceList
     * @param targetClass
     * @param <S>
     * @param <T>
     * @return
     * @throws BeansException
     */
    public static <S, T> List<T> beanList2List(List<S> sourceList, Class<T> targetClass) throws IllegalArgumentException, InstantiationException, IllegalAccessException {
        Assert.notNull(sourceList, "sourceList must not be null");
        Assert.notNull(targetClass, "targetClass must not be null");
        List<T> targetList = new ArrayList<T>();
        for (S source : sourceList) {
            T target = targetClass.newInstance();
            BeanUtils.copyProperties(source, target);
            targetList.add(target);
        }
        return targetList;
    }

    /**
     * map集合 转简单实体集合
     *
     * @param list
     * @param cls
     * @return obj
     */
    public static List<Object> convertListMap(List<Map<String, Object>> list, Class cls) throws Exception {
        List<Object> objList = new ArrayList<Object>(100);
        for (Map<String, Object> map : list) {
            objList.add(convertMap(map, cls));
        }
        return objList;
    }

    public static Map<String, Object> BeanToMap(Object bean, Map map) throws Exception {
        if (bean == null) {
            return map;
        }
        Class<?> actualEditable = bean.getClass();
        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(bean) : null;
            if ("orders".equalsIgnoreCase(key) && null != value && value instanceof List) {
                List<OrderItem> oiArray = (List<OrderItem>) value;
                for (int i = 0; i < oiArray.size(); i++) {
                    map.put("orders[" + i + "].column", oiArray.get(i).getColumn());
                    map.put("orders[" + i + "].asc", oiArray.get(i).isAsc());
                }
            } else {
                map.put(key, value);
            }
        }
        return map;
    }

    public static Map<String, Object> BeanToMap(Object bean, Map map, Boolean flag) throws Exception {
        if (bean == null) {
            return map;
        }
        Class<?> actualEditable = bean.getClass();
        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(bean) : null;
            if (flag) {
                key = key.substring(0, 1).toUpperCase().concat(key.substring(1));
            }
            if ("orders".equalsIgnoreCase(key) && null != value && value instanceof List) {
                List<OrderItem> oiArray = (List<OrderItem>) value;
                for (int i = 0; i < oiArray.size(); i++) {
                    map.put("orders[" + i + "].column", oiArray.get(i).getColumn());
                    map.put("orders[" + i + "].asc", oiArray.get(i).isAsc());
                }
            } else {
                map.put(key, value);
            }
        }
        return map;
    }

    /**
     * Bean转Map，如果bean 属性里面有空字符串替换为null
     */
    public static Map<String, Object> BeanToMapChangeStr2Null(Object bean, Map map) throws Exception {
        if (bean == null) {
            return map;
        }
        Class<?> actualEditable = bean.getClass();
        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(bean) : null;

            if ("orders".equalsIgnoreCase(key) && null != value && value instanceof List) {
                List<OrderItem> oiArray = (List<OrderItem>) value;
                for (int i = 0; i < oiArray.size(); i++) {
                    map.put("orders[" + i + "].column", oiArray.get(i).getColumn());
                    map.put("orders[" + i + "].asc", oiArray.get(i).isAsc());
                }
            } else {
                if (value instanceof String && "".equals(value)) {
                    map.put(key, null);
                } else {
                    map.put(key, value);
                }
            }
        }
        return map;
    }

    /**
     * Bean转Map，如果bean 属性里面有空字符串替换为null
     * flag 首字母是否大写
     */
    public static Map<String, Object> BeanToMapChangeStr2Null(Object bean, Map map, boolean flag) throws Exception {
        if (bean == null) {
            return map;
        }
        Class<?> actualEditable = bean.getClass();
        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter != null ? getter.invoke(bean) : null;
            if (flag) {
                key = key.substring(0, 1).toUpperCase().concat(key.substring(1));
            }

            if ("orders".equalsIgnoreCase(key) && null != value && value instanceof List) {
                List<OrderItem> oiArray = (List<OrderItem>) value;
                for (int i = 0; i < oiArray.size(); i++) {
                    map.put("orders[" + i + "].column", oiArray.get(i).getColumn());
                    map.put("orders[" + i + "].asc", oiArray.get(i).isAsc());
                }
            } else {
                if (value instanceof String && "".equals(value)) {
                    map.put(key, null);
                } else {
                    map.put(key, value);
                }
            }
        }
        return map;
    }

    /**
     * 处理page对象的descs 和ascs问题
     *
     * @param bean
     * @param map
     * @return
     * @throws Exception
     */
    public static Map<String, Object> PageToMap(Object bean, Map map) throws Exception {
        if (bean == null) {
            return map;
        }
        Class<?> actualEditable = bean.getClass();
        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("size") == 0 || key.compareToIgnoreCase("current") == 0 || key.compareToIgnoreCase("orders") == 0) {
                Method getter = property.getReadMethod();
                Object value = getter != null ? getter.invoke(bean) : null;
                if ("orders".equalsIgnoreCase(key) && null != value && value instanceof List) {
                    List<OrderItem> oiArray = (List<OrderItem>) value;
                    for (int i = 0; i < oiArray.size(); i++) {
                        map.put("orders[" + i + "].column", oiArray.get(i).getColumn());
                        map.put("orders[" + i + "].asc", oiArray.get(i).isAsc());
                    }
                } else {
                    map.put(key, value);
                }
            }
        }
        return map;
    }
}
